package call_handler;
import java.util.List;
import java.util.ArrayList;

public class CallHandler {
    private static CallHandler instance;
    private final int LEVELS = Rank.TOTAL_LEVELS;
    private final int[] numbers = {20, 6, 2};
    // Hierachy for repondents levels
    List<List<Employee>> employeeLevels;

    // queues for call ranks
    List<List<Call>> callQueues;

    protected CallHandler() {
	assert numbers.length == LEVELS;
	employeeLevels = new ArrayList<List<Employee>>(LEVELS);
	callQueues = new ArrayList<List<Call>>(LEVELS);
	for (int i = 0; i < LEVELS; i++) {
	    List<Employee> respondents = new ArrayList<Employee>();
	    for (int j = 0; j < numbers[i]; j++) {
		respondents.add(new Employee(i));
	    }
	    employeeLevels.add(respondents);
	}
    }

    /**
     * GQ.
     * cannot use synchronized(this) in a static context.
     * remove keyword static here.
     */
    public 
    static 
    CallHandler getInstance() {
	if (instance == null) {
		
		/**
		 * GQ.
		 * Use CallHandler.class instead.
		 */
//		synchronized (this) {
	    synchronized(CallHandler.class) {
		if (instance == null) {
		    instance = new CallHandler();
		}
	    }
	}
	return instance;
    }

    public void dispatchCall(Caller caller) {
	Call call = new Call(caller);
	dispatchCall(call);
    }

    public void dispatchCall(Call call) {
	Employee emp = getHandlerForCall(call);
	if (emp != null) {
	    emp.receiveCall(call);
	    call.setHandler(emp);
	} else {
	    call.reply("Please wait for a free representative to reply.");
	    List<Call> queue = ensureCallList(call.getRank().getValue());
	}
    }

    private List<Call> ensureCallList(int level) {
	List<Call> queue = callQueues.get(level);
	if (queue == null) {
	    queue = new ArrayList<Call>();
	    callQueues.add(level, queue);
	}
	return queue;
    }

    public Employee getHandlerForCall(Call call) {
	int level = call.getRank().getValue();
	List<Employee> employeeLevel = employeeLevels.get(level);
	for (Employee emp : employeeLevel) {
	    if (emp.isFree()) {
		return emp;
	    }
	}
	return null;
    }

    public boolean assignCall(Employee emp) {
	int level = emp.getRank().getValue();
	List<Call> queue = callQueues.get(level);
	if (queue != null && !queue.isEmpty()) {
	    Call call = queue.remove(0);
	    if (call != null) {
		emp.receiveCall(call);
		/**
		 * GQ.
		 * CtCI didn't set Handler for this call.
		 */
		call.setHandler(emp);
		return true;
	    }
	}
	return false;
    }
}